import gc

gc.collect()
start_mem = gc.mem_free()

'''
ломает все

if "execfile" not in globals():
    def execfile(file):
        with open(file) as file:
            return exec(file.read())
    globals()["execfile"] = execfile
'''

KERNEL_VERSION = "0.11"
KERNEL_API_COMPATIBLE = ["0.1", "0.2", "0.3", "0.4", "0.4A", "0.5", "0.6", "0.7", "0.8", "0.9", "0.10", "0.10A", "0.11"]
KERNEL_TYPE = 1
KERNEL_NAME = "Rubik"
KERNEL_VERSION_NAME = "Transpicuous Tarantula"
BASE_KERNEL_PATH = "/"
AUTORUN_PATH = BASE_KERNEL_PATH + 'kernel/autorun'

COMPILED_VERSION = False # для версий которые собраны при помощи кросскомпилятора байткода
USED_NATIVE_MODULES = False # для версий где используются декораторы @micropython.native

import os, sys, json
arch = [None, 'x86', 'x64', 'armv6', 'armv6m', 'armv7m', 'armv7em', 'armv7emsp', 'armv7emdp','xtensa', 'xtensawin'][sys.implementation._mpy >> 10]

bits = 0
v = sys.maxsize
while v:
    bits += 1
    v >>= 1

PLATFORM_BITS = bits + 1
PLATFORM_ARCH = arch
PLATFORM_ISVM = False
PLATFORM_BOARD = sys.platform
PLATFORM = f'{PLATFORM_BOARD} ({PLATFORM_ARCH}) {PLATFORM_BITS} bit'

os.chdir(BASE_KERNEL_PATH + 'kernel/')

sys.path.append(BASE_KERNEL_PATH + 'kernel/')
sys.path.append(BASE_KERNEL_PATH)
sys.path.append(BASE_KERNEL_PATH + 'kernel/modules')
sys.path.append(BASE_KERNEL_PATH + 'kernel/modules/temp')
sys.path.append(BASE_KERNEL_PATH + 'kernel/modules/camera')
sys.path.append(BASE_KERNEL_PATH + 'kernel/modules/air_quality')
sys.path.append(BASE_KERNEL_PATH + 'kernel/modules/ethernet')
sys.path.append(BASE_KERNEL_PATH + 'kernel/modules/servo')


from Logger import Logger
from singleton import singleton

@singleton
class SystemLogger(Logger):
    Prefix = "[ Kernel ]"
    Separator = "::"
    LogFormat = f'%prefix% %separator% %text%'
    def __init__(self): pass

import machine, sys, gc, os

print('\033[2J') # Clear screen
SystemLogger().log("Staring Rubik3 kernel for QubicOS...")

config = {
    "tmp": {
        "enabled": False,
        "size": 25
    },

    "network": {
        "enabled": False,
    },

    "init": {
        "enabled": True,
        "app": "ush.py",
        "startgccontainer": False,
        "telnet": False,
    },

    "ulp": {
        "enabled": False
    },

    "autostartenabled": True,
    "memdumpatpanic": True,
    
    "overclocking": {
        "cpu_freq": False
    }
}

# TODO: модули ядра

try:
    config = json.load(open(BASE_KERNEL_PATH + "kernel/config/kernel_conf.json"))
    SystemLogger().log("Using kernel_config.json")
except Exception as e:
    SystemLogger().log(f"Cannot load kernel config: {e}. Using default config")

SystemLogger().log(f"Detected platform: {PLATFORM} CPU @ {machine.freq() // 1000000} MHz")
SystemLogger().log(f"Byteorder: {sys.byteorder}")
SystemLogger().log(f"Machine ID: {machine.unique_id()}")
SystemLogger().log(f'Detected arch: {arch}')

if config["overclocking"]["cpu_freq"]: 
    SystemLogger().log(f'CPU Overclocking enabled. New CPU freq: {config["overclocking"]["cpu_freq"]}')
    machine.freq(config["overclocking"]["cpu_freq"])

THREADING_AVAILABLE = False

try: 
    SystemLogger().log("Initializing Container subsystem")

    import _thread
    from Containers.Container import ContainerPool
    THREADING_AVAILABLE = True
except:
    text = f'''NOTICE:
Sorry, but this system({PLATFORM}) is not supporting threading.
Container subsystem will be unavailable.
'''

    [SystemLogger().log(s) for s in text.split('\n')]
#TODO: сделать рефакторинг

try:
    import zlib
    
    # Проверка наличия функций decompress и compress
    if not hasattr(zlib, "decompress") or not hasattr(zlib, "compress"):
        SystemLogger().log("This system is not supporting gzip/zlib compression. Use software implementation instead")
        
except ImportError:
    SystemLogger().log("This system is not supporting gzip/zlib compression. Use software implementation instead")

if not "tmp" in os.listdir('/') and config["tmp"]["enabled"]:
    try:
        SystemLogger().log("Mounting /tmp ram disk")
        from BlockDevices.RAMFS import RAMBlockDev

        TMP_SIZE = config["tmp"]["size"]

        bdev = RAMBlockDev(512, 2 * TMP_SIZE) #  ~25 KB
        os.VfsFat.mkfs(bdev)
        os.mount(bdev, '/tmp')

        SystemLogger().log("/tmp Mounted")
    except:
        SystemLogger().log("Failed mounting /tmp")
else:
    SystemLogger().log("/tmp already mounted")

if config["network"]["enabled"] and THREADING_AVAILABLE:
    SystemLogger().log("Starting NetStart Container")

    ContainerPool().CreateContainer("netstart_service", lambda _: execfile(BASE_KERNEL_PATH + "apps/netstart.py"))
    ContainerPool().GetContainer("netstart_service").start([])

    SystemLogger().log("Configuring network...")

SystemLogger().log("Freeing unused kernel image (initmem) memory...")
import gc
freed = gc.collect()
SystemLogger().log(f"Total free mem: {gc.mem_free()}")

ULP_SUBSYSTEM_ENABLED = config["ulp"]["enabled"]
if ULP_SUBSYSTEM_ENABLED:
    SystemLogger().log("Initializing Ultra Low Power CoProcessor(ULP) system...")
    import ULP

data = f"""
CPU        : {PLATFORM}
CPU MHz    : {machine.freq() / 1000000}
ID         : {machine.unique_id()}
""" # добавить платформо-зависимые сборки

if config["tmp"]["enabled"]:
    for line in data.split('\n'):
        if not line == '': SystemLogger().log(line)

    with open('/tmp/cpuinfo', 'w') as file:
        file.write(data)

GC_COLLECT_PERIOD_MS = 2500

import time

def gc_collect(_):
    gc.collect()
    time.sleep(GC_COLLECT_PERIOD_MS)

if config["init"]["startgccontainer"] and THREADING_AVAILABLE:
    SystemLogger().log("Starting GC Service container")

    ContainerPool().CreateContainer("gc_service", gc_collect)
    ContainerPool().GetContainer("gc_service").start()

#SystemLogger().log("Starting FTP server")
#ContainerPool().CreateContainer("uftpd", execfile)
#ContainerPool().GetContainer("uftpd").start([BASE_KERNEL_PATH + "apps/ftpservice.py"])

WELCOME = f'Rubik3 3.{KERNEL_VERSION[2:]} {KERNEL_VERSION_NAME} Kernel for QubicOS, {PLATFORM}, Build #147'

SystemLogger().log(WELCOME)

SystemLogger().log("Starting system Registry")
execfile(BASE_KERNEL_PATH + 'kernel/Services/Registry.py', globals(), locals())

SystemLogger().log("Starting Advanced File Abstraction Layer")
from AFAL.FileManager import *

if config["autostartenabled"]:
    SystemLogger().log("Starting auto start apps...")

    files = os.listdir(AUTORUN_PATH)

    for file in files:
        try:
            execfile(AUTORUN_PATH + "/" + file)
        except Exception as e:
            SystemLogger().log(f"System excepted non-fatal error at run auto start apps: {e}")

if config["init"]["enabled"]:
    SystemLogger().log("Starting init_app from kernel_config")
    SystemLogger().log(f"Free mem: {gc.mem_free()}, kernel size in RAM: {start_mem - gc.mem_free()}")

    #ContainerPool().CreateContainer("ushell_service", lambda: execfile(BASE_KERNEL_PATH + "apps/ush.py", {"syscall": Pipe, "BASE_KERNEL_PATH": BASE_KERNEL_PATH}))
    #ContainerPool().GetContainer("ushell_service").start([])
    try:
        execfile(BASE_KERNEL_PATH + "apps/" + config["init"]["app"])
        SystemLogger().log(f"System Halted, init_app exited.")
    except Exception as e:
        SystemLogger().log(f"System Halted, init_app performed an illegal operation and was stopped. error: {e}")
        import micropython, machine, time
        
        if config['memdumpatpanic']:
            SystemLogger().log("Dumping memory...")
            micropython.mem_info(True)
        SystemLogger().log("System will automatic reset in 20 seconds.")
        time.sleep(20)
        machine.reset()
else:
    SystemLogger().log("System Halted, no init_app in kernel_config.")

REST = False

while True:
    machine.idle() if REST else time.sleep(1)

